﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Reflection;
using System.Threading;

namespace UnitTestsScenarioEditor
{
    [TestClass]
    public class UnitTestTerrain
    {
        #region Private Fields

        private static EngineInit engineForTest;
        private static Scenario_Editor.Tools.Terrain.Terrain terrain3x3;
        private static Scenario_Editor.Tools.Terrain.Terrain terrain4x4;

        #endregion Private Fields

        #region Public Methods

        [ClassInitialize()]
        public static void ClassInitialize(TestContext testcontext)
        {
            try
            {
                UnitTestTerrain.engineForTest = new EngineInit();

                terrain3x3 = new Scenario_Editor.Tools.Terrain.Terrain(3, 3, 10, 0, engineForTest.engine, engineForTest.materialManager, engineForTest.shaderManager, engineForTest.camera, 100000);

                terrain4x4 = new Scenario_Editor.Tools.Terrain.Terrain(3, 3, 10, 0, engineForTest.engine, engineForTest.materialManager, engineForTest.shaderManager, engineForTest.camera, 100000);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Assert.Fail("Can´t initialize terrain.");
            }
        }

        [ClassCleanup()]
        public static void CleanupClass()
        {
            Console.WriteLine("Class Cleanup");
        }

        public static void Copy(string sourceDirectory, string targetDirectory)
        {
            DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
            DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);

            CopyAll(diSource, diTarget);
        }

        public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
        {
            Directory.CreateDirectory(target.FullName);

            // Copy each file into the new directory.
            foreach (FileInfo fi in source.GetFiles())
            {
                Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
                fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
            }

            // Copy each subdirectory using recursion.
            foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
            {
                DirectoryInfo nextTargetSubDir =
                    target.CreateSubdirectory(diSourceSubDir.Name);
                CopyAll(diSourceSubDir, nextTargetSubDir);
            }
        }

        [TestCleanup()]
        public void Cleanup()
        {
            Console.WriteLine("Test Cleanup");
        }

        [TestInitialize()]
        public void Initialize()
        {
            Console.WriteLine("For each Test TestMethodInit");
        }

        [TestMethod()]
        public void TestGetSectorCoordinate()
        {
            string propertyName = "SECTORSIZE";
            FieldInfo fieldInfo = typeof(Scenario_Editor.Tools.Terrain.Terrain).GetField(propertyName, BindingFlags.NonPublic | BindingFlags.Static);
            float sectorSize = (float)fieldInfo.GetValue(null);
            float offset = 1.0f;

            // Terrain 3x3

            // South West
            PrivateObject objTerrain3x3 = new PrivateObject(terrain3x3);
            // Die unterste linke Ecke.
            var retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 3.0f / 2.0f, 0, -sectorSize * 3.0f / 2.0f) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 0));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 2.0f / 2.0f - offset, 0, -sectorSize * 2.0f / 2.0f - offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 0));
            // Die obere linke Ecke.
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 3.0f / 2.0f, 0, sectorSize * 3.0f / 2.0f - offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 2));
            // Die unterste rechte Ecke.
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(sectorSize * 3.0f / 2.0f - offset, 0, -sectorSize * 3.0f / 2.0f) });
            Assert.AreEqual(retVal, new System.Drawing.Point(2, 0));
            // Die obere rechte Ecke.
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(sectorSize * 3.0f / 2.0f - offset, 0, sectorSize * 3.0f / 2.0f - offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(2, 2));
            // das westliche feld
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 2.0f / 2.0f + offset, 0, 0) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 1));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 2.0f / 2.0f + offset, 0, sectorSize / 2.0f - offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 1));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 2.0f / 2.0f + offset, 0, -sectorSize / 2.0f) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 1));
            // Ausserhalb obere linke Ecke.
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-sectorSize * 3.0f / 2.0f, 0, sectorSize * 3.0f / 2.0f) });
            Assert.AreEqual(retVal, new System.Drawing.Point(0, 3));
            // Mitte
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-offset, 0, -offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(1, 1));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(offset, 0, -offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(1, 1));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(-offset, 0, offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(1, 1));
            retVal = objTerrain3x3.Invoke("GetSectorCoordinate", new object[] { new SharpDX.Vector3(offset, 0, offset) });
            Assert.AreEqual(retVal, new System.Drawing.Point(1, 1));
        }

        [TestMethod()]
        [Timeout(60000)]
        public void TestMultiThreading()
        {
            PrivateObject objTerrain3x3 = new PrivateObject(terrain3x3);
            // Die unterste linke Ecke.
            terrain3x3.StartUpdateThread();
            int i = 0;
            while (i < 100)
            {
                Console.WriteLine("UpdateAllSectors : " + i);
                objTerrain3x3.Invoke("UpdateAllSectors", new object[] { null });
                Thread.Sleep(100);
                i++;
            }

            int numberOfCurentUpdatedSectors = 0;

            do
            {
                numberOfCurentUpdatedSectors = 0;
                for (int x = 0; x < terrain3x3.nSectorsX; x++)
                {
                    for (int y = 0; y < terrain3x3.nSectorsY; y++)
                    {
                        if (terrain3x3.sector[x, y].MustUpdated)
                            numberOfCurentUpdatedSectors++;
                    }
                }
            } while (numberOfCurentUpdatedSectors != 0);

            terrain3x3.StopUpdateThread();
        }

        [TestMethod()]
        public void TestXmlMaterialStorage()
        {
            try
            {
                string fileName = "materials.xml";

                Engine.Graphics.MaterialStorage materialSorage = new Engine.Graphics.MaterialStorage();

                var material1 = new WorldRacing2.Material.Wr2Material(0, 0, 0, Engine.Graphics.EnumMappingMode.material00, engineForTest.materialManager,
                    new WorldRacing2.Material.MappingsWR2(0, 90, 0, 1, 1),
                    new WorldRacing2.Material.MappingsWR2(90, 0, 0, 2, 2),
                    new WorldRacing2.Material.MappingsWR2(0, 0, 90, 3, 3));

                var material2 = new WorldRacing2.Material.Wr2Material(0, 0, 0, Engine.Graphics.EnumMappingMode.material00, engineForTest.materialManager,
                    new WorldRacing2.Material.MappingsWR2(2, 90, 2, 1, 1),
                    new WorldRacing2.Material.MappingsWR2(90, 2, 2, 2, 2),
                    new WorldRacing2.Material.MappingsWR2(2, 2, 90, 3, 3));

                materialSorage.AddMaterial("World Racing 2", "Material1", material1);
                materialSorage.AddMaterial("World Racing 2", "Material2", material2);
                materialSorage.AddMaterial("C11UC", "Material1", material1);
                materialSorage.AddMaterial("C11UC", "Material2", material2);

                materialSorage.SaveToFile(fileName);

                var newMaterialStorage = Engine.Graphics.MaterialStorage.FromFile(fileName, engineForTest.engine.addinManager, engineForTest.materialManager);

                WorldRacing2.Material.Wr2Material material1Loaded = (WorldRacing2.Material.Wr2Material)materialSorage.GetMaterial("World Racing 2", "Material1");

                Assert.AreEqual(material1Loaded.TextureID1, material1.TextureID1);
                Assert.AreEqual(material1Loaded.TextureID2, material1.TextureID2);
                Assert.AreEqual(material1Loaded.TextureID3, material1.TextureID3);
                Assert.AreEqual(material1Loaded.MappingMode, material1.MappingMode);
                Assert.AreEqual(material1Loaded.Mappings1.CRC, material1.Mappings1.CRC);
                Assert.AreEqual(material1Loaded.Mappings2.CRC, material1.Mappings2.CRC);
                Assert.AreEqual(material1Loaded.Mappings3.CRC, material1.Mappings3.CRC);

                WorldRacing2.Material.Wr2Material material2Loaded = (WorldRacing2.Material.Wr2Material)materialSorage.GetMaterial("World Racing 2", "Material2");

                Assert.AreEqual(material2Loaded.TextureID1, material2.TextureID1);
                Assert.AreEqual(material2Loaded.TextureID2, material2.TextureID2);
                Assert.AreEqual(material2Loaded.TextureID3, material2.TextureID3);
                Assert.AreEqual(material2Loaded.MappingMode, material2.MappingMode);
                Assert.AreEqual(material2Loaded.Mappings1.CRC, material2.Mappings1.CRC);
                Assert.AreEqual(material2Loaded.Mappings2.CRC, material2.Mappings2.CRC);
                Assert.AreEqual(material2Loaded.Mappings3.CRC, material2.Mappings3.CRC);

                // No tests for C11 Materials so far...
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.Message);
            }
        }

        #endregion Public Methods
    }
}